﻿#include "C3fdGeomTurboFileReader.h"
#include <IVtkTools_ShapeDataSource.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRep_Builder.hxx>
#include <Geom_BSplineCurve.hxx>
#include <gp_Ax1.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <gp_Trsf.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Shape.hxx>
#include "Common/C3fdProject.h"
#include <QFileInfo>
#include "MainWindow/MainWindow.h"
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkPolyDataWriter.h>
#include <vtkRenderWindow.h>
#include "Settings/GraphOption.h"
#include "Settings/BusAPI.h"
#include <QDir>

namespace MainWidget
{

	C3fdGeomTurboFileReader::C3fdGeomTurboFileReader(GUI::MainWindow *mw, const QStringList &files, bool readHubAndShroud)
		: ThreadTask(mw), m_Files(files), m_ReadHubAndShroud(readHubAndShroud)
	{
		connect(this, SIGNAL(showMessage(ModuleBase::Message)), mw, SIGNAL(printMessageToMessageWindow(ModuleBase::Message)));
	}
	C3fdGeomTurboFileReader::~C3fdGeomTurboFileReader()
	{
	}

	void C3fdGeomTurboFileReader::run()
	{
		ModuleBase::ThreadTask::run();

		parse(m_Files);

		ModuleBase::ThreadTask::threadTaskFinished();
	}
	void C3fdGeomTurboFileReader::setRenderer(vtkSmartPointer<vtkRenderer> renderer)
	{
		m_Renderer = renderer;
	}

	void C3fdGeomTurboFileReader::parse(QStringList files)
	{
		int total = files.size();
		int i = 0;
		for (auto file : files)
		{
			emit showInformation(tr("[%1/%2]Import Geometry File From \"%3\"").arg(++i).arg(total).arg(file));
			QHash<QString, TopoDS_Shape> shapes = {};
			if (file.isEmpty())
			{
				return;
			}
			QFile infile(file);
			if (!infile.exists())
			{
				return;
			}

			if (!infile.open(QIODevice::ReadOnly))
			{
				return;
			}

			// 清空之前的数据
			m_Hub.clear();
			m_Shroud.clear();
			m_Suction.clear();
			m_Pressure.clear();
			m_NumOfBlades = 0;
			m_Tolerance = 1e-006;

			QString line = "";
			bool result = true;

			while (result && !infile.atEnd())
			{
				line = QString(infile.readLine()).simplified().toLower();
				if (line.startsWith("geometric_tolerance"))
				{
					emit showInformation(tr("Parse geometric tolerance"));
					bool ok;
					double num = line.replace("geometric_tolerance", "").simplified().toDouble(&ok);
					if (ok)
					{
						m_Tolerance = num;
					}
				}
				else if (line.startsWith("number_of_blades"))
				{
					emit showInformation(tr("Parse number of blades"));
					bool ok;
					int num = line.replace("number_of_blades", "").simplified().toInt(&ok);
					if (ok)
					{
						m_NumOfBlades = num;
					}
				}
				else if (line.startsWith("hub") && m_ReadHubAndShroud)
				{
					emit showInformation(tr("Parse hub data"));
					result = parseHubData(infile);
				}
				else if (line.startsWith("shroud") && m_ReadHubAndShroud)
				{
					emit showInformation(tr("Parse shroud data"));
					result = parseShroudData(infile);
				}
				else if (line.startsWith("suction"))
				{
					emit showInformation(tr("Parse suction data"));
					result = parseSuctionData(infile);
				}
				else if (line.startsWith("pressure"))
				{
					emit showInformation(tr("Parse pressure data"));
					result = parsePressureData(infile);
				}
			}

			if (!result)
				return;

			int numOfSection = m_Suction.size();
			if (numOfSection != m_Pressure.size())
			{
				return;
			}

			gp_Ax1 zAxis = gp::OZ();

			if (m_ReadHubAndShroud)
			{

				// 叶根hub
				emit showInformation(tr("Reconstruct hub data"));
				TopoDS_Shape hubEdge = createSpline(m_Hub);
				auto *hubShape = new TopoDS_Shape;
				*hubShape = BRepPrimAPI_MakeRevol(hubEdge, zAxis, Standard_False);
				shapes.insert("hub", *hubShape);
				// 叶顶shroud
				emit showInformation(tr("Reconstruct shroud data"));
				TopoDS_Shape shroudEdge = createSpline(m_Shroud);
				auto *shroudShape = new TopoDS_Shape;
				*shroudShape = BRepPrimAPI_MakeRevol(shroudEdge, zAxis, Standard_False);
				shapes.insert("shroud", *shroudShape);
			}

			// 叶片
			emit showInformation(tr("Reconstruct blade data"));
			BRepOffsetAPI_ThruSections bladeSections = new BRepOffsetAPI_ThruSections(true, false, m_Tolerance);
			for (int i = 0; i < numOfSection; ++i)
			{
				TopoDS_Edge suctionEdge = createSpline(m_Suction.at(i));
				TopoDS_Edge pressureEdge = createSpline(m_Pressure.at(i));
				TopoDS_Wire bladeSectionWire = BRepBuilderAPI_MakeWire(suctionEdge, pressureEdge);
				bladeSections.AddWire(bladeSectionWire);
			}
			bladeSections.Build();

			if (bladeSections.IsDone())
			{
				auto *bladeShape = new TopoDS_Shape;
				*bladeShape = bladeSections.Shape();
				shapes.insert("blade", *bladeShape);
			}

			if (m_NumOfBlades > 0)
			{
				emit showInformation(tr("Duplicate blade data"));
				double angleBetweenTwoBlade = 2 * M_PI / m_NumOfBlades;
				gp_Trsf aTrsf;
				TopoDS_Compound aRes;
				BRep_Builder aBuilder;
				aBuilder.MakeCompound(aRes);
				for (int i = 1; i < m_NumOfBlades; ++i)
				{
					double angle = angleBetweenTwoBlade * i;
					aTrsf.SetRotation(zAxis, angle);
					BRepBuilderAPI_Transform aBRespTrsf(bladeSections.Shape(), aTrsf, true);
					aBRespTrsf.Build();
					if (aBRespTrsf.IsDone())
					{
						aBuilder.Add(aRes, aBRespTrsf.Shape());
					}
				}

				auto *duplicatedBladeShape = new TopoDS_Shape;
				*duplicatedBladeShape = aRes;
				shapes.insert("blades", *duplicatedBladeShape);

				auto *currentCase = Common::C3fdProject::instance()->getCurrentCase();
				if (currentCase != nullptr)
				{
					QFileInfo info(file);
					const QString name = info.fileName();
					auto *geom = new Common::GeomTurbo;
					geom->setFileName(name);
					geom->setNumOfBlades(m_NumOfBlades);
					if (m_ReadHubAndShroud)
					{
						geom->setParts({ "Hub", "Shroud", "Blade", "Blades" });
					}
					else
					{
						geom->setParts({ "Blade", "Blades" });
					}
					currentCase->addGeomTurboFile(geom);
				}
			}

			Setting::GraphOption *option = Setting::BusAPI::instance()->getGraphOption();
			QColor geomcolor = option->getGeometrySurfaceColor();

			for (const auto &key : shapes.keys())
			{
				emit showInformation(tr("Perpare to render Geometry for ") + key);
				vtkNew<IVtkTools_ShapeDataSource> shapeDataSource;
				shapeDataSource->SetShape(new IVtkOCC_Shape(shapes.value(key)));
				vtkNew<vtkPolyDataMapper> mapper;
				mapper->SetInputConnection(shapeDataSource->GetOutputPort());
				IVtkTools::InitShapeMapper(mapper);

				QFileInfo info(file);
				const QString name = info.fileName() + "-" + key.at(0).toUpper() + key.mid(1);

				vtkNew<vtkActor> actor;
				actor->SetMapper(mapper);
				actor->GetProperty()->SetColor(geomcolor.redF(), geomcolor.greenF(), geomcolor.blueF());

				actor->SetObjectName(name.toStdString());

				if (m_Renderer != nullptr)
				{
					m_Renderer->AddActor(actor);
				}
				QString path = Common::C3fdProject::instance()->getCurrentProjectDir() + Common::C3fdProject::instance()->getCacheDir() + QDir::separator();

				// 缓存几何模型
				vtkNew<vtkPolyDataWriter> writer;
				writer->SetFileName(QString("%1geom_%2-%3.vtk").arg(path).arg(info.completeBaseName()).arg(key).toStdString().c_str());
				writer->SetInputConnection(shapeDataSource->GetOutputPort());
				writer->SetFileTypeToBinary();
				writer->Update();
				writer->Write();
			}
			m_ReadHubAndShroud = false;
		}
		emit finishRead();
	}

	bool C3fdGeomTurboFileReader::parseHubData(QFile &file)
	{
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2;

		// ZR
		file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 2)
				{
					sendErrorMessage(tr("Error Format in hub data: Length!"));
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in hub data: Z!"));
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in hub data: R!"));
					result = false;
					break;
				}
				m_Hub.push_back(zrToXyz(v1, v2));
			}
		}
		else
		{
			// 发生错误，退出程序
			sendErrorMessage(tr("Error Format in hub data!"));
			result = false;
		}
		return result;
	}

	bool C3fdGeomTurboFileReader::parseShroudData(QFile &file)
	{
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2;

		// ZR
		file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 2)
				{
					sendErrorMessage(tr("Error Format in shroud data: Length!"));
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in shroud data: Z!"));
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in shroud data: R!"));
					result = false;
					break;
				}
				m_Shroud.push_back(zrToXyz(v1, v2));
			}
		}
		else
		{
			// 发生错误，退出程序

			sendErrorMessage(tr("Error Format in shroud data!"));

			result = false;
		}
		return result;
	}

	bool C3fdGeomTurboFileReader::parseSuctionData(QFile &file)
	{
		bool tempOk, result = true;
		int tempNum = 0;
		QString line = "";
		QList<QList<double>> section;

		// SECTIONAL
		file.readLine();
		// num of section
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			while (tempNum > 0)
			{
				line = QString(file.readLine()).simplified().toLower();
				// if (line.startsWith("#section"))
				if (line.trimmed().toLower() == "xyz")
				{
					section = parseSectionData(file, &tempOk);
					if (tempOk)
					{
						m_Suction.push_back(section);
					}
					--tempNum;
				}
			}
		}
		else
		{
			// 发生错误，退出程序
			sendErrorMessage(tr("Error Format in suction data!"));
			result = false;
		}
		return result;
	}

	bool C3fdGeomTurboFileReader::parsePressureData(QFile &file)
	{
		bool tempOk, result = true;
		int tempNum = 0;
		QString line = "";
		QList<QList<double>> section;

		// SECTIONAL
		file.readLine();
		// num of section
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			while (tempNum > 0)
			{
				line = QString(file.readLine()).simplified().toLower();
				// if (line.startsWith("#section"))
				if (line.trimmed().toLower() == "xyz")
				{
					section = parseSectionData(file, &tempOk);
					if (tempOk)
					{
						m_Pressure.push_back(section);
					}
					--tempNum;
				}
			}
		}
		else
		{
			// 发生错误，退出程序
			sendErrorMessage(tr("Error Format in pressure data!"));
			result = false;
		}
		return result;
	}

	// （Z=轴向，R=径向）
	// （X=径向，Y=周向，Z=轴向）
	QList<double> C3fdGeomTurboFileReader::zrToXyz(double z, double r)
	{
		QList<double> p;
		p.push_back(r);
		p.push_back(0.0);
		p.push_back(z);
		return p;
	}

	QList<QList<double>> C3fdGeomTurboFileReader::parseSectionData(QFile &file, bool *ok)
	{
		QList<QList<double>> section;
		int tempNum = 0;
		bool tempOk;
		bool result = true;
		double v1, v2, v3;

		// XYZ
		// file.readLine();
		// num of points
		tempNum = file.readLine().simplified().toInt(&tempOk);
		if (tempOk)
		{
			for (int i = 0; i < tempNum; ++i)
			{
				auto values = file.readLine().simplified().split(' ');
				if (values.length() != 3)
				{
					sendErrorMessage(tr("Error Format in section data: Length!"));
					result = false;
					break;
				}
				v1 = values.at(0).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in section data: X!"));
					result = false;
					break;
				}
				v2 = values.at(1).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in section data: Y!"));
					result = false;
					break;
				}
				v3 = values.at(2).toDouble(&tempOk);
				if (!tempOk)
				{
					sendErrorMessage(tr("Error Format in section data: Z!"));
					result = false;
					break;
				}
				QList<double> p;
				p.push_back(v1);
				p.push_back(v2);
				p.push_back(v3);

				section.push_back(p);
			}
		}
		else
		{
			// 发生错误，退出程序
			sendErrorMessage(tr("Error Format in section data!"));
			result = false;
		}
		if (ok != nullptr)
		{
			*ok = result;
		}
		return section;
	}

	// 该方法生成的B样条曲线有突变点
	/*TopoDS_Edge C3fdGeomTurboFileReader::createSpline(QList<QList<double>> points)
	{
		/// 均匀B样条，节点向量中的节点值成等差排布
		/// 均匀B样条的基函数呈周期性，即所有的基函数有相同的形状
		/// 每个后续基函数仅仅市前面基函数在新位置上的重复
		TopoDS_Edge anEdge;
		QVector<gp_Pnt> pointsArr;
		for (auto point : points)
		{
			if (point.size() == 3)
			{
				pointsArr.push_back(gp_Pnt(point.at(0), point.at(1), point.at(2)));
			}
			else
			{
				continue;
			}
		}
		int size = pointsArr.length();
		TColgp_Array1OfPnt array1(1, size);
		for (int i = 0; i < size; i++)
		{
			array1.SetValue(i + 1, pointsArr[i]);
		}
		GeomAPI_PointsToBSpline outline(array1);
		if (outline.IsDone())
		{
			Handle(Geom_BSplineCurve) hCurve = outline.Curve();
			anEdge = BRepBuilderAPI_MakeEdge(hCurve);
		}
		return anEdge;
	}*/

	TopoDS_Edge C3fdGeomTurboFileReader::createSpline(QList<QList<double>> points)
	{
		TopoDS_Edge anEdge;
		QVector<gp_Pnt> pointsArr;
		for (auto point : points)
		{
			if (point.size() == 3)
			{
				pointsArr.push_back(gp_Pnt(point.at(0), point.at(1), point.at(2)));
			}
			else
			{
				continue;
			}
		}
		int size = pointsArr.length();

		Handle(TColgp_HArray1OfPnt) pts = new TColgp_HArray1OfPnt(1, size);

		for (int i = 0; i < size; i++)
		{
			pts->SetValue(i + 1, pointsArr[i]);
		}
		GeomAPI_Interpolate interp(pts, Standard_False, 1e-6);
		gp_Vec v(0.0, 0.0, 0.0);
		TColgp_Array1OfVec vtan(v, 1, size);
		Handle(TColStd_HArray1OfBoolean) flags = new TColStd_HArray1OfBoolean(1, size, false);
		// define tangents and flags for every pole, any data is ok, not used.
		interp.Load(vtan, flags, Standard_False);
		interp.Perform();

		if (interp.IsDone())
		{
			Handle(Geom_BSplineCurve) hCurve = interp.Curve();
			anEdge = BRepBuilderAPI_MakeEdge(hCurve);
		}
		return anEdge;
	}

	void C3fdGeomTurboFileReader::sendErrorMessage(QString message)
	{
		ModuleBase::Message msg;
		msg.type = Common::Message::Error;
		msg.message = message;
		emit showMessage(msg);
	}

}